﻿using System.Collections.Generic;
using System.Linq;
using Ewk.MachineLearning.Genetic.Programming.Program;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Ewk.MachineLearning.Genetic.Programming.UnitTests.Program
{
    [TestClass]
    public class FunctionNodeUnitTest : ProgramUnitTestBase
    {
        [TestMethod]
        public void A_FunctionNode_Can_Multiply_Two_Or_More_Nodes()
        {
            var m = new FunctionNode(
                FunctionFactory.CreateNumericToNumericFunction(objects => objects.Aggregate((o1, o2) => o1 * o2), 3, "*"))
                        {
                            ChildNodes = new List<Node>
                                             {
                                                 new ConstantNode(Three),
                                                 new ConstantNode(Five),
                                                 new ConstantNode(Seven)
                                             }
                        };
            var result = m.GetValue();

            Assert.AreEqual(3*5*7, result);
            Assert.AreEqual("(* 3 5 7)", m.ToString());
        }

        [TestMethod]
        public void A_FunctionNode_Can_Multiply_Two_Or_More_Nodes_Of_Different_Type()
        {
            var m = new FunctionNode(
                FunctionFactory.CreateNumericToNumericFunction(objects => objects.Aggregate((o1, o2) => o1 * o2), 3, "*"))
                        {
                            ChildNodes = new List<Node>
                                             {
                                                 new ConstantNode(Three),
                                                 new FunctionNode(
                                                     FunctionFactory.CreateNumericToNumericFunction(objects => objects.Aggregate((o1, o2) => o1 + o2), 2, "+"))
                                                     {
                                                         ChildNodes = new List<Node>
                                                                          {
                                                                              new ConstantNode(Five),
                                                                              new ConstantNode(Seven)
                                                                          }
                                                     }
                                             }
                        };
            var result = m.GetValue();

            Assert.AreEqual(3*(5 + 7), result);
            Assert.AreEqual("(* 3 (+ 5 7))", m.ToString());
        }

        [TestMethod]
        public void A_FunctionNode_Can_Perform_String_Functions_On_Two_Or_More_Nodes()
        {
            var m =
                new FunctionNode(
                    FunctionFactory.CreateNumericToStringFunction(objects => objects.Aggregate((o1, o2) => o1.ToString() + " " + o2.ToString()), 3, "+"))
                    {
                        ChildNodes = new List<Node>
                                         {
                                             new ConstantNode(Three),
                                             new ConstantNode(Five),
                                             new ConstantNode(Seven)
                                         }
                    };
            var result = m.GetValue();

            Assert.AreEqual(3.ToString() + " " + 5.ToString() + " " + 7.ToString(), result);
            Assert.AreEqual("(+ 3 5 7)", m.ToString());
        }

        [TestMethod]
        public void A_FunctionNode_Can_Perform_String_Functions_On_Two_Or_More_Nodes_Of_Different_Type()
        {
            var m =
                new FunctionNode(
                    FunctionFactory.CreateNumericToStringFunction(objects => objects.Aggregate((o1, o2) => o1.ToString() + " " + o2.ToString()), 2, "+"))
                    {
                        ChildNodes = new List<Node>
                                         {
                                             new ConstantNode(Three),
                                             new FunctionNode(
                                                 FunctionFactory.CreateNumericToNumericFunction(objects => objects.Aggregate((o1, o2) => o1 + o2), 2, "+"))
                                                 {
                                                     ChildNodes = new List<Node>
                                                                      {
                                                                          new ConstantNode(Five),
                                                                          new ConstantNode(Seven)
                                                                      }
                                                 }
                                         }
                    };
            var result = m.GetValue();

            Assert.AreEqual(3.ToString() + " " + (5 + 7).ToString(), result);
            Assert.AreEqual("(+ 3 (+ 5 7))", m.ToString());
        }

        [TestMethod]
        public void When_A_FunctionNode_Is_Cloned_Its_Children_Are_Also_Cloned()
        {
            var three = new ConstantNode(Three);
            var five = new ConstantNode(Five);
            var seven = new ConstantNode(Seven);
            var addFunction = new FunctionNode(
                FunctionFactory.CreateNumericToNumericFunction(objects => objects.Aggregate((o1, o2) => o1 + o2), 2, "+"))
                                  {
                                      ChildNodes = new List<Node>
                                                       {
                                                           five,
                                                           seven
                                                       }
                                  };

            var m =
                new FunctionNode(
                    FunctionFactory.CreateNumericToStringFunction(objects => objects.Aggregate((o1, o2) => o1.ToString() + " " + o2.ToString()), 2, "+"))
                    {
                        ChildNodes = new List<Node>
                                         {
                                             three,
                                             addFunction
                                         }
                    };
            var result = m.Clone() as FunctionNode;

            Assert.IsNotNull(result);
            Assert.AreNotEqual(m, result);
            Assert.IsFalse(result.ChildNodes.Contains(addFunction));
        }
    }
}